package org.xxpay.manage.common.ctrl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.multipart.MultipartFile;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.*;
import org.xxpay.core.common.vo.DBApplicationConfig;
import org.xxpay.core.common.vo.DBUploadFileConfig;
import org.xxpay.core.entity.SysUser;
import org.xxpay.manage.common.config.MainConfig;
import org.xxpay.manage.common.service.CommonService;
import org.xxpay.manage.secruity.JwtUser;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author: dingzhiwei
 * @date: 17/12/6
 * @description:
 */
@Controller
public class BaseController {

    private static final MyLog logger = MyLog.getLog(BaseController.class);

    private static final int DEFAULT_PAGE_INDEX = 1;
    private static final int DEFAULT_PAGE_SIZE = 20;
    protected static final String PAGE_COMMON_ERROR = "common/error";

    /** BaseController 直接注入request 2020-08-19 by ttf **/
    @Autowired
    protected HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;

    @Autowired
    protected MainConfig mainConfig;

    @Autowired
    public CommonService commonService;

    protected DBApplicationConfig getDBApplicationConfig(){
        return commonService.sysConfigService.getDBApplicationConfig();
    }

    protected JwtUser getUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        return jwtUser;
    }

    protected int getPageIndex(JSONObject object) {
        if(object == null) return DEFAULT_PAGE_INDEX;
        Integer pageIndex = object.getInteger("page");
        if(pageIndex == null) return DEFAULT_PAGE_INDEX;
        return pageIndex;
    }

    protected int getPageSize(JSONObject object) {
        if(object == null) return DEFAULT_PAGE_SIZE;
        Integer pageSize = object.getInteger("limit");
        if(pageSize == null) return DEFAULT_PAGE_SIZE;
        return pageSize;
    }

    protected int getPageIndex(Integer page) {
        if(page == null) return DEFAULT_PAGE_INDEX;
        return page;
    }

    protected int getPageSize(Integer limit) {
        if(limit == null) return DEFAULT_PAGE_SIZE;
        return limit;
    }

    protected JSONObject getJsonParam(HttpServletRequest request) {
        String params = request.getParameter("params");
        if(StringUtils.isNotBlank(params)) {
            return JSON.parseObject(params);
        }
        // 参数Map
        Map properties = request.getParameterMap();
        // 返回值Map
        JSONObject returnObject = new JSONObject();
        Iterator entries = properties.entrySet().iterator();
        Map.Entry entry;
        String name;
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            if(null == valueObj){
                value = "";
            }else if(valueObj instanceof String[]){
                String[] values = (String[])valueObj;
                for(int i=0;i<values.length;i++){
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length()-1);
            }else{
                value = valueObj.toString();
            }

            if(!name.contains("[")){
                returnObject.put(name, value);
                continue;
            }
            //添加对json对象解析的支持  example: {ps[abc] : 1}
            String mainKey = name.substring(0, name.indexOf("["));
            String subKey = name.substring(name.indexOf("[") + 1 , name.indexOf("]"));
            JSONObject subJson = new JSONObject();
            if(returnObject.get(mainKey) != null) {
                subJson = (JSONObject)returnObject.get(mainKey);
            }
            subJson.put(subKey, value);
            returnObject.put(mainKey, subJson);
        }
        return returnObject;
    }

    /** 方法重载 2020-08-19 by ttf **/
    protected JSONObject getJsonParam() {
        return getJsonParam(request);
    }

    protected String getString(JSONObject param, String key) {
        if(param == null) return null;
        return param.getString(key);
    }

    protected String getStringRequired(JSONObject param, String key) {
        if(param == null ) {
            throw new RuntimeException(getErrMsg(key));
        }
        String value = param.getString(key);
        if(StringUtils.isBlank(value)) {
            throw new RuntimeException(getErrMsg(key));
        }
        return value;
    }

    protected String getStringDefault(JSONObject param, String key, String defaultValue) {
        String value = getString(param, key);
        if(value == null) return defaultValue;
        return value;
    }

    protected Byte getByte(JSONObject param, String key) {
        if(param == null) return null;
        return param.getByte(key);
    }

    protected Byte getByteRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Byte value = param.getByte(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected int getByteDefault(JSONObject param, String key, byte defaultValue) {
        Byte value = getByte(param, key);
        if(value == null) return defaultValue;
        return value.byteValue();
    }

    protected Integer getInteger(JSONObject param, String key) {
        if(param == null) return null;
        return param.getInteger(key);
    }

    protected Integer getIntegerRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Integer value = param.getInteger(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected int getIntegerDefault(JSONObject param, String key, int defaultValue) {
        Integer value = getInteger(param, key);
        if(value == null) return defaultValue;
        return value.intValue();
    }

    protected Long getLong(JSONObject param, String key) {
        if(param == null) return null;
        return param.getLong(key);
    }

    protected Long getLongRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Long value = param.getLong(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected long getLongDefault(JSONObject param, String key, long defaultValue) {
        Long value = getLong(param, key);
        if(value == null) return defaultValue;
        return value.longValue();
    }

    protected JSONObject getJSONObject(JSONObject param, String key) {
        if(param == null) return null;
        return param.getJSONObject(key);
    }

    protected <T> T getObject(JSONObject param, String key, Class<T> clazz) {
        JSONObject object = getJSONObject(param, key);
        if(object == null) return null;
        return JSON.toJavaObject(object, clazz);
    }

    protected <T> T getObject(JSONObject param, Class<T> clazz) {
        if(param == null) return null;
        return JSON.toJavaObject(param, clazz);
    }

    private String getErrMsg(String key) {
        return "参数" + key + "必填";
    }

    /**
     * 参数值不能为空
     * @param param
     * @param keys
     */
    protected void checkRequired(JSONObject param, String... keys) {
        if(param == null ) throw new RuntimeException(getErrMsg(keys[0]));
        for(String key : keys) {
            String value = param.getString(key);
            if(value == null) throw new RuntimeException(getErrMsg(key));
        }
    }

    /**
     * 参数值不能为空,不能以"-"开头
     * @param param
     * @param keys
     */
    public void checkRequired2(JSONObject param, String... keys) {
        if(param == null ) throw new RuntimeException(getErrMsg(keys[0]));
        for(String key : keys) {
            String value = param.getString(key);
            if(value == null) throw new RuntimeException(getErrMsg(key));
            if(value.startsWith("-")) throw new RuntimeException("参数" + key + "不能小于0");
        }
    }

    /**
     * 得到前端传入的金额元,转换成长整型分
     * @param param
     * @param name
     * @return
     */
    public Long getRequiredAmountL(JSONObject param, String name) {
        String amountStr = getStringRequired(param, name);  // 前端填写的为元,可以为小数点2位
        Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).longValue(); // // 转成分
        return amountL;
    }

    /**
     * 处理参数中的金额(将前端传入金额元转成分)
     * @param param
     * @param names
     */
    public void handleParamAmount(JSONObject param, String... names) {
        for(String name : names) {
            String amountStr = getString(param, name);  // 前端填写的为元,可以为小数点2位
            if(StringUtils.isNotBlank(amountStr)) {
                Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).longValue(); // // 转成分
                param.put(name, amountL);
            }
        }
    }

    public Map<String, Object> request2payResponseMap(HttpServletRequest request, String[] paramArray) {
        Map<String, Object> responseMap = new HashMap<>();
        for (int i = 0;i < paramArray.length; i++) {
            String key = paramArray[i];
            String v = request.getParameter(key);
            if (v != null) {
                responseMap.put(key, v);
            }
        }
        return responseMap;
    }

    /**
     * 生成公共查询参训
     * @param param
     * @return
     */
    public JSONObject getQueryObj(JSONObject param) {
        // 订单起止时间
        Date createTimeStart = null;
        Date createTimeEnd = null;
        String createTimeStartStr = getString(param, "createTimeStart");
        if(StringUtils.isNotBlank(createTimeStartStr)) createTimeStart = DateUtil.str2date(createTimeStartStr);
        String createTimeEndStr = getString(param, "createTimeEnd");
        if(StringUtils.isNotBlank(createTimeEndStr)) createTimeEnd = DateUtil.str2date(createTimeEndStr);
        JSONObject queryObj = new JSONObject();
        queryObj.put("createTimeStart", createTimeStart);
        queryObj.put("createTimeEnd", createTimeEnd);
        return queryObj;
    }

    /**
     * 校验平台超级密码
     * @param passWord
     * @return
     */
    public boolean checkMgrSuperPwd(String passWord) {
        String mgrSuperPwd = MD5Util.string2MD5(passWord);
        JSONObject jsonObject = commonService.sysConfigService.getSysConfig();
        String superPwd = (String) jsonObject.get("mgrSuperPwd");
        if (StringUtils.isNotBlank(superPwd)) {
            if (superPwd.equals(mgrSuperPwd)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否超级管理员
     * @return
     */
    public boolean isSupAdmin() {
        return this.getUser().getIsSuperAdmin() == MchConstant.PUB_YES;
    }

    /**
     * 输出excel数据
     * @param fileName
     * @param data
     */
    protected void writeExcelStream(String fileName, List<List> data) {

        try {
            response.setHeader("Content-disposition", "attachment;filename="
                    + new String((fileName + ".xlsx").getBytes("gb2312"), "ISO8859-1"));//设置文件头编码格式
            response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8");//设置类型

            Workbook workbook = new XSSFWorkbook(); //生成excel 2007格式
            Sheet sheet = workbook.createSheet();

            for(int i = 0; i < data.size(); i++){
                Row row = sheet.createRow(i);

                for(int j = 0; j< data.get(i).size(); j++){
                    Cell cell = row.createCell(j);
                    if(data.get(i).get(j) != null) {
                        cell.setCellValue(data.get(i).get(j).toString());
                    }
                }
            }
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            logger.error("writeExcelStream", e);
        }
    }

    /** 验证谷歌验证码 */
    protected void checkGoogleCode(Long userId, Long googleCode) {

        SysUser user = commonService.sysService.findByUserId(userId);
        GoogleAuthenticatorUtil.checkByThrowException(googleCode, user.getGoogleAuthSecretKey());
    }

    /** 验证用户安全认证方式 **/
    protected void checkSecurityReq(JSONObject params){

        if(params == null){
            params = getJsonParam();
        }

        Long googleCode = params.getLong("C_SECURITYREQ_googleCode");
        String supPassword = params.getString("C_SECURITYREQ_supPassword");

        SysUser current = commonService.sysService.findByUserId(getUser().getId());
        if(current.getSecurityCheckType() == MchConstant.MGR_SECURITY_CHECK_TYPE_NONE){  //无需验证

            return ;

        }else if(current.getSecurityCheckType() == MchConstant.MGR_SECURITY_CHECK_TYPE_SUPPASSWORD){ //超级密码

            if(StringUtils.isEmpty(supPassword)) throw new ServiceException(RetEnum.RET_MGR_SUPER_PASSWORD_NOT_MATCH);
            if(!new BCryptPasswordEncoder().matches(supPassword, current.getSupPassword())) {
                throw new ServiceException(RetEnum.RET_MGR_SUPER_PASSWORD_NOT_MATCH);
            }

        }else if(current.getSecurityCheckType() == MchConstant.MGR_SECURITY_CHECK_TYPE_GOOGLECODE){ //谷歌验证

            GoogleAuthenticatorUtil.checkByThrowException(googleCode, current.getGoogleAuthSecretKey());

        }else if(current.getSecurityCheckType() == MchConstant.MGR_SECURITY_CHECK_TYPE_SUP_AND_GOOGLE){ //谷歌 + 超级密码

            GoogleAuthenticatorUtil.checkByThrowException(googleCode, current.getGoogleAuthSecretKey());

            if(StringUtils.isEmpty(supPassword)) throw new ServiceException(RetEnum.RET_MGR_SUPER_PASSWORD_NOT_MATCH);
            if(!new BCryptPasswordEncoder().matches(supPassword, current.getSupPassword())) {
                throw new ServiceException(RetEnum.RET_MGR_SUPER_PASSWORD_NOT_MATCH);
            }

        }else{
            throw new ServiceException(RetEnum.RET_MGR_SECURITY_TYPE_CONFIG_ERROR);
        }


    }

    /** 将上传的文件进行保存 - 公共函数 **/
    protected void saveFile(MultipartFile file, String savePath) throws Exception {

        File saveFile = new File(savePath);

        //如果文件夹不存在则创建文件夹
        File dir = saveFile.getParentFile();
        if(!dir.exists()) dir.mkdirs();
        file.transferTo(saveFile);
    }

    /** 保存上传的文件， 返回文件http下载地址， 仅上传 无需鉴权文件使用  **/
    protected String commonSaveUpdFile(MultipartFile file, String saveDir, String saveFileName) throws Exception{

        if(saveFileName == null) {  //如果名称为null, 使用UUID格式
            String suffix = FileUtils.getFileSuffix(file.getOriginalFilename(), false).toLowerCase();
            saveFileName = UUID.randomUUID() + "." + suffix;
        }

        DBUploadFileConfig dbUploadFileConfig = commonService.sysConfigService.getDBUploadFileConfig();

        //获取上传地址配置信息
        if(MchConstant.UPDFILE_SAVE_LOCAL.equals(dbUploadFileConfig.getUploadFileSaveType())){ //上传文件存储位置： 本地

            //本地方式，暂时 不支持saveDir

            //2. 保存文件
            String savePath = mainConfig.getAttachmentRootPath()+"/"+ saveFileName;
            saveFile(file, savePath);

            //3. 返回响应地址
            return getDBApplicationConfig().getMgrSiteUrl()+"/imgs/"+saveFileName;

        }else if(MchConstant.UPDFILE_SAVE_ALIYUN_OSS.equals(dbUploadFileConfig.getUploadFileSaveType())){ //上传文件存储位置： 阿里云oss存储

            //oss配置信息参数
            String ossBucketName = dbUploadFileConfig.getOssBucketName();
            String ossEndpoint = dbUploadFileConfig.getOssEndpoint();
            String objectName = saveFileName;
            if(StringUtils.isNotEmpty(saveDir)){
                objectName = saveDir + "/" + saveFileName;
            }

            // 创建OSSClient实例
            OSS ossClient = new OSSClientBuilder().build(ossEndpoint, dbUploadFileConfig.getOssAccessKeyId(), dbUploadFileConfig.getOssAccessKeySecret());

            // 上传文件。
            ossClient.putObject(ossBucketName, objectName, file.getInputStream());

            // 关闭OSSClient。
            ossClient.shutdown();

            return"https://" + ossBucketName + "." + ossEndpoint + "/" + objectName;
        }else{
            throw ServiceException.build(RetEnum.RET_SERVICE_UPLOAD_CONFIG_ERROR);
        }
    }


}
